// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "cd.h"
#include "ChildView.h"

#include <cmath>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const double _PI = 3.1415926535897932384626433832795;

//
// The Radius of a CD
//

const double Radius = 4.70 / 2.;		// Radius
const double HOLE = 0.60 / 2.;		// Radius
const double THICK = 0.045;


// CChildView

CChildView::CChildView()
{
	m_camera.Set(10, 10, 20, 0, 0, 0, 0, 1, 0);
	m_camera.MouseMode(CGrCamera::PITCHYAW);

	m_topTex.LoadFile("CDtop.bmp");
    m_botTex.LoadFile("CDbot.bmp");

	SetDoubleBuffer(true);

	m_wireframe = false;
}

CChildView::~CChildView()
{
}


BEGIN_MESSAGE_MAP(CChildView, COpenGLWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_COMMAND(ID_VIEW_WIREFRAME, OnViewWireframe)
	ON_UPDATE_COMMAND_UI(ID_VIEW_WIREFRAME, OnUpdateViewWireframe)
END_MESSAGE_MAP()



// CChildView message handlers

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!COpenGLWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);

	return TRUE;
}

// Notice how I used two simple functions defined here to automate a task
// I need to do for four vertices at a time.

//
// Name :         CDTopVertex()
// Description :  This is used to indicate a vertex on the top of the CD.
//                The texture s,t are automatically computed from the
//                x, z values.
//

void CDTopVertex(double x, double y, double z)
{
    double s = (x - (-Radius)) / (2 * Radius);
    double t = (-z - (-Radius)) / (2 * Radius);

    glTexCoord2d(s, t);
	glVertex3d(x, y, z);
}

//
// Name :         CDBotVertex()
// Description :  This is used to indicate a vertex on the bottom of the CD.
//                The texture s,t are automatically computed from the
//                x, z values. This is different from the top because the
//                z direction is reversed when looking at the CD from the bottom.
//

void CDBotVertex(double x, double y, double z)
{
    double s = (x - (-Radius)) / (2 * Radius);
    double t = (z - (-Radius)) / (2 * Radius);      // -z for top

    glTexCoord2d(s, t);
	glVertex3d(x, y, z);
}


void CChildView::OnGLDraw(CDC * pDC)
{
	glPolygonMode(GL_FRONT, m_wireframe ? GL_LINE : GL_FILL);

    glClearColor(1, 1, 1, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//
	// Set up the camera
	//

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Determine the screen Radius so we can determine the aspect ratio
	int width, height;
	GetSize(width, height);
	GLdouble aspectratio = GLdouble(width) / GLdouble(height);

	// Set the camera parameters
	gluPerspective(15., // Vertical field of view in degrees.
	aspectratio, // The aspect ratio.
	20., // Near clipping
	1000.); // Far clipping

	//
	// Some standard parameters
	//

	// Enable depth test
	glEnable(GL_DEPTH_TEST);

	// Cull backfacing polygons
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);

	// Set the camera location
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	m_camera.gluLookAt();

	// Enable lighting
	glEnable(GL_LIGHTING);

	float dimd = 0.5f;
	GLfloat dim[] = {dimd, dimd, dimd, 1.0f};
	GLfloat brightwhite[] = {1.f, 1.f, 1.f, 1.0f};

	GLfloat lightpos0[] = {1.f, 0.5f, 1.2f, 0.f};

	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_POSITION, lightpos0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, brightwhite);
	glLightfv(GL_LIGHT0, GL_AMBIENT, dim);

	GLfloat white[] = {1.f, 1.f, 1.f, 1.f};

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white);


	const int STEPS = 32;

	// 
	// CD Top
	// 

	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBindTexture(GL_TEXTURE_2D, m_topTex.TexName()); 

	for(int i=0;  i<STEPS;  i++)
	{
		double angle1 = double(i) / double(STEPS) * _PI * 2.;
		double angle2 = double(i + 1) / double(STEPS) * _PI * 2.;

		double c1 = cos(angle1);
		double s1 = sin(angle1);
		double c2 = cos(angle2);
		double s2 = sin(angle2);

		glBegin(GL_POLYGON);

		glNormal3d(0, 1, 0);

        CDTopVertex(c1 * HOLE, 0, -s1 * HOLE);
        CDTopVertex(c1 * Radius, 0, -s1 * Radius);
        CDTopVertex(c2 * Radius, 0, -s2 * Radius);
        CDTopVertex(c2 * HOLE, 0, -s2 * HOLE);

		glEnd();
	}

	//
	// CD Bottom
	//

	glBindTexture(GL_TEXTURE_2D, m_botTex.TexName()); 

	for(int i=0;  i<STEPS;  i++)
	{
		double angle1 = double(i) / double(STEPS) * _PI * 2.;
		double angle2 = double(i + 1) / double(STEPS) * _PI * 2.;

		double c1 = cos(angle1);
		double s1 = sin(angle1);
		double c2 = cos(angle2);
		double s2 = sin(angle2);

		glBegin(GL_POLYGON);

		glNormal3d(0, -1, 0);
        CDBotVertex(c2 * HOLE, -THICK, -s2 * HOLE);
        CDBotVertex(c2 * Radius, -THICK, -s2 * Radius);
        CDBotVertex(c1 * Radius, -THICK, -s1 * Radius);
        CDBotVertex(c1 * HOLE, -THICK, -s1 * HOLE);

		glEnd();
	}

	glDisable(GL_TEXTURE_2D);

    //
	// Edges
    //

	for(int i=0;  i<STEPS;  i++)
	{
		double angle1 = double(i) / double(STEPS) * _PI * 2.;
		double angle2 = double(i + 1) / double(STEPS) * _PI * 2.;

		double c1 = cos(angle1);
		double s1 = sin(angle1);
		double c2 = cos(angle2);
		double s2 = sin(angle2);

		// outside edge
		glBegin(GL_POLYGON);
		glNormal3d(c1, 0, -s1);
		glVertex3d(c1 * Radius, 0, -s1 * Radius);
		glVertex3d(c1 * Radius, -THICK, -s1 * Radius);
		glNormal3d(c2, 0, -s2);
		glVertex3d(c2 * Radius, -THICK, -s2 * Radius);
		glVertex3d(c2 * Radius, 0, -s2 * Radius);
		glEnd();

		// Inside edge
		glBegin(GL_POLYGON);
		glNormal3d(-c2, 0, s2);
		glVertex3d(c2 * HOLE, 0, -s2 * HOLE);
		glVertex3d(c2 * HOLE, -THICK, -s2 * HOLE);
		glNormal3d(-c1, 0, s1);
		glVertex3d(c1 * HOLE, -THICK, -s1 * HOLE);
		glVertex3d(c1 * HOLE, 0, -s1 * HOLE);
		glEnd();

	}


	glFlush();
}

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	m_camera.MouseDown(point.x, point.y);


	COpenGLWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	if(nFlags & MK_LBUTTON)
	{
		m_camera.MouseMove(point.x, point.y);
		Invalidate();
	}

	COpenGLWnd::OnMouseMove(nFlags, point);
}

void CChildView::OnViewWireframe()
{
	m_wireframe = !m_wireframe;
	Invalidate();
}

void CChildView::OnUpdateViewWireframe(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_wireframe);
}
